import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
import * as path from "path";
import { CallerMethod } from "../src/client/typings";
import { ProtoGrpcType } from "./proto/sample";

const PROTO_PATH = path.resolve(__dirname, "./proto/sample.proto");

const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
  keepCase: true,
  longs: String,
  enums: String,
  defaults: true,
  oneofs: true,
});
const protoDescriptor = grpc.loadPackageDefinition(
  packageDefinition
) as unknown as ProtoGrpcType;

async function main() {
  const client = new protoDescriptor.helloworld.Greeter(
    "localhost:50051",
    grpc.credentials.createInsecure()
  );

  client.SayHello({ name: "你的名字" }, (err, reply) => {
    if (err) console.log(err);
    console.log(`[SayHello]: `, reply?.message);
  });
  const h = await call(client.sayHello, { name: "" });

  const callC = client.sayHelloC((err, reply) => {
    if (err) console.log(err);
    console.log(`[sayHelloC]: `, reply?.message);
  });
  const messages = ["abc", "def", "ghi"];
  for (const name of messages) {
    callC.write({ name });
  }
  callC.end();
  const callS = client.sayHelloS({ name: "lumi" });
  callS.on("data", (chunk) => {
    console.log("callS", chunk);
  });
  callS.on("end", () => {
    console.log("callS is end");
  });

  const callB = client.sayHelloB();
  callB.on("data", (chunk) => {
    console.log("callB", chunk);
  });
  callB.on("end", () => {
    console.log("callB is end");
  });
  for (const name of messages) {
    callB.write({ name });
  }
  callB.end();
}

main();

type GrpcFunction<I, O> = (
  argument: I,
  metadata: grpc.Metadata,
  options: grpc.CallOptions,
  callback: (
    error?: grpc.ServiceError | undefined,
    result?: O | undefined
  ) => void
) => grpc.ClientUnaryCall;

async function call<I, O>(
  grpcFunction: CallerMethod<I, O>,
  argument: I,
  metadata?: grpc.Metadata,
  options?: grpc.CallOptions
): Promise<O> {
  const args = [argument, metadata, options].filter((it) => !!it);
  return new Promise((resolve, reject) => {
    (grpcFunction as any)(
      ...args,
      (err: grpc.ServiceError | undefined, result: O | undefined) => {
        if (err) reject(err);
        if (result) resolve(result);
      }
    );
  });
}
